#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import os
import sys
import copy
import time
import urllib
from optparse import OptionParser, Option

from config import Config
from utils import _exec_remote

config = Config()

# Decorators for actions
def args(*args, **kwargs):
    def _decorator(func):
        func.__dict__.setdefault('options', []).insert(0, (args, kwargs))
        return func
    return _decorator

class Balance_command(object):
    def __init__(self):
        self.flag = "parallel_balance"
        self.parallel = os.path.join(config.shm, "balance/parallel")
        self.parallel_expdata = os.path.join(config.shm, "balance/parallel_expdata")

    @args('-p', '--paralle', dest="paralle", metavar='<int>', type='int')
    @args('-e', '--expdata', dest="expdata", metavar='<int>', type='int', help='timeout seconds, exp: 300')
    def set(self, paralle, expdata):
        args = {"p": self.parallel, "pd": os.path.dirname(self.parallel),
                'pe': self.parallel_expdata, "ped": os.path.dirname(self.parallel_expdata),
                'paralle': paralle, "expdata": expdata + int(time.time()),
                }
        cmd = """
                mkdir -p %(pd)s;
                mkdir -p %(ped)s;
                echo %(paralle)s > %(p)s;
                echo %(expdata)s > %(pe)s
            """ % (args)
        hosts = config.hosts.keys()
        for h in hosts:
            try:
                stdout, stderr = _exec_remote(h, cmd)
            except Exception, e:
                print 'set', h, 'error', e
            else:
                print 'set', h, stdout.strip(), stderr.strip()

    def get(self):
        hosts = config.hosts.keys()
        cmd = "lich configdump|grep %s" % (self.flag)
        for h in hosts:
            try:
                stdout, stderr = _exec_remote(h, cmd)
            except Exception, e:
                print h, 'error', e
            else:
                print h, stdout.strip(), stderr.strip()

class Recover_command(Balance_command):
    def __init__(self):
        self.flag = "parallel_recover"
        self.parallel = os.path.join(config.shm, "/recover/parallel")
        self.parallel_expdata = os.path.join(config.shm, "recover/parallel_expdata")

def match(name, key_value_tuples):
    for (k, v) in key_value_tuples:
        if k.lower() == name:
            return v
    sys.exit(2)

def methods_of(obj):
    """Get all callable methods of an object that 
    don't start with underscore
    returns a list of tuples of the form (method_name, method)"""
    result = []
    for i in dir(obj):
        if callable(getattr(obj, i)) and not i.startswith('_'):
            result.append((i, getattr(obj, i)))
    return result

CATEGORIES = [
        ('balance', Balance_command),
        ('recover', Recover_command),
        ]

class MyOption(Option):
    ACTIONS = Option.ACTIONS + ("extend",)
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)
    def take_action(self, action, dest, opt, value, values, parser):
        if action == "extend":
            lvalue = value.split(" ")
            values.ensure_value(dest, []).extend(lvalue)
        else:
             Option.take_action(self, 
                     action, dest, opt, value, values, parser)

def main():
    argv = copy.copy(sys.argv)
    script_name = argv.pop(0)
    if len(argv) < 1:
            print
            print script_name + ' category action [<argv>]'
            print 'Available categories:'
            for k, _v in CATEGORIES:
                print '\t%s'%(k)
            sys.exit(2)

    category = argv.pop(0)
    command_object = match(category, CATEGORIES)()
    actions = methods_of(command_object)
    fn = ''
    if len(argv) < 1:
        if hasattr(command_object, '__call__'):
            action = ''
            fn = command_object.__call__
        else:
            print script_name + " category action [<args>]"
            print "Available actions for %s category:" % category
            for k, _v in actions:
                print "\t%s" % k
            sys.exit(2)
    else:
        action = argv.pop(0)
        fn = getattr(command_object, action)

    options = getattr(fn, 'options', [])

    usage = "%%prog %s %s <args> [options]" % (category, action)
    parser = OptionParser(usage=usage, option_class=MyOption)
    for ar, kw in options:
        parser.add_option(*ar, **kw)

    (opts, fn_args) = parser.parse_args(argv)
    fn_kwargs = vars(opts)
    for k, v in fn_kwargs.items():
        if v is None:
            del fn_kwargs[k]
    try:
        fn(*fn_args, **fn_kwargs)
        sys.exit(0)
    except TypeError:
        print "Possible wrong number of arguments supplied"
        #print fn.__doc__
        parser.print_help()
    except Exception:
        print "Command failed, please check log for more info"
        raise

if __name__ == "__main__":
    main()
